/*
 * Copyright (C) 2014
 *
 * Brick Yang <printfxxx@163.com>
 *
 * This program is free software. You can redistribute it and/or
 * modify it as you like.
 */

/**
 * @file	netdev.h
 * @brief	Header file for netdev
 */

#ifndef _NETDEV_H_
#define _NETDEV_H_

#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/ktime.h>
#include <linux/workqueue.h>

#include "time.h"
#include "worker.h"
#include "ring.h"
#include "cmd.h"

#define SB_SKB_PAD	SKB_DATA_ALIGN(256)

typedef struct flow {
	bool start, fc_mode;
	struct netdev *netdev;
	struct sk_buff **pool;
	plat_time_t burst_invl, burst_prev;
	u32 fc_burst;
	s64 burst_remain;
	u64 tx_pkts, tx_bytes, rx_pkts, rx_bytes,
	    pkt_cnt, pkt_remain, fc_rate;
	unsigned int pool_used, pool_sz, q_used, q_halt,
		     qlen, qth, qwt, tx_head;
	unsigned long *txq_bitmap;
	seqcount_t tx_stats_seq, rx_stats_seq;
	struct list_head node;
} flow_t;

typedef struct netdev_stats {
	u64 pkts, bytes;
} netdev_stats_t;

typedef struct netdev_priv_ops {
	void (*pcpu_flow_poll)(flow_t *);
	unsigned int (*netdev_tx_burst)(struct net_device *, struct sk_buff **, unsigned int nr);
	int (*netdev_ioctl)(struct net_device *, proto_handle_t *, proto_rxd_t *);
	int (*netdev_force_stop)(struct net_device *);
} netdev_priv_ops_t;

typedef struct netdev {
	unsigned long flags;
#define NETDEV_F_ATTACHED	0
#define NETDEV_F_STARTED	1
#define NETDEV_F_RXDUMP		2
#define NETDEV_F_DEVSTATS	3
	struct net_device *ndev;
	netdev_priv_ops_t *priv_ops;
	unsigned int ndev_flags_set, ndev_flags_clr;
	netdev_features_t ndev_features_set, ndev_features_clr;
	struct net_device_ops hook;
	const struct net_device_ops *netdev_ops;
	struct packet_type pt;
	struct delayed_work dwork;
	unsigned long dwork_flags;
#define DW_F_SET_START_STATS	0
#define DW_F_SET_STOP_STATS	1
#define DW_F_SET_FORCE_STOP	2
#define DW_F_CLR_STATS		3
	u64 tx_pps_rt, tx_Bps_rt, tx_bps_rt,
	    rx_pps_rt, rx_Bps_rt, rx_bps_rt;
	ktime_t time_prev, time_start, time_stop, time_force_stop;
	seqcount_t stats_seq;
	netdev_stats_t tx_base, tx_prev, tx_start, tx_stop,
		       rx_base, rx_prev, rx_start, rx_stop;
	__percpu flow_t *pcpu_flow;
	struct list_head node;
} netdev_t;

typedef struct netdev_link_state {
	unsigned int link;
	unsigned int autoneg;
	int speed;
	int duplex;
} netdev_link_state_t;

int __init netdev_add_all(void);
void netdev_del_all(void);

netdev_t *sb_netdev_get_by_name(const char *name);
struct sk_buff *sb_netdev_alloc_skb(netdev_t *netdev, unsigned int len, gfp_t gfp_mask);
void sb_netdev_kfree_skb(netdev_t *netdev, struct sk_buff *skb);
bool sb_netdev_in_filter(const char *name);
struct net_device *sb_netdev_alloc(int sizeof_priv, unsigned int txqs, unsigned int rxqs);
void sb_netdev_free(struct net_device *ndev);
int sb_netdev_register(struct net_device *ndev);
void sb_netdev_unregister(struct net_device *ndev);
void sb_netdev_receive(struct sk_buff *skb, struct net_device *ndev);
int sb_netdev_notify(struct net_device *ndev, unsigned long event, void *arg);
struct sk_buff *sb_alloc_skb(struct net_device *ndev, unsigned int len, gfp_t gfp_mask);
void sb_kfree_skb(struct sk_buff *skb);
void *sb_baddr_to_skbh(struct net_device *ndev, dma_addr_t baddr);

static inline struct sk_buff *sb_skbh_to_skb(void *head)
{
	return head ? head - SKB_DATA_ALIGN(sizeof(struct sk_buff)) : NULL;
}

static inline dma_addr_t sb_skbh_to_baddr(void *head)
{
	const dma_addr_t *baddr;

	baddr = (void *)sb_skbh_to_skb(head) - SKB_DATA_ALIGN(sizeof(baddr));

	return *baddr;
}

#ifdef MTRACE

#define MTRACE_SB_SKB_ADD(p)			(_mtrace_skb_add((long)p, __func__, __LINE__) ? (sb_kfree_skb(p), p = NULL, -ENOMEM) : 0)
#define MTRACE_SB_SKB_DEL(p)			_mtrace_skb_del((long)p, __func__, __LINE__)
#define MTRACE_SB_NETDEV_SKB_ADD(p, d)		(_mtrace_skb_add((long)p, __func__, __LINE__) ? ((d)->priv_ops ? sb_kfree_skb(p) : consume_skb(p),	\
						 p = NULL, -ENOMEM) : 0)
#define MTRACE_SB_NETDEV_SKB_DEL(p)		_mtrace_skb_del((long)p, __func__, __LINE__)

#else	/* MTRACE */

#define MTRACE_SB_SKB_ADD(p)			(0)
#define MTRACE_SB_SKB_DEL(p)			do { } while (0)
#define MTRACE_SB_NETDEV_SKB_ADD(p, d)		(0)
#define MTRACE_SB_NETDEV_SKB_DEL(p)		do { } while (0)

#endif	/* MTRACE */

#endif	/* _NETDEV_H_ */
